using System;
using System.Linq;
using System.Web.Mvc;
using System.Web.Security;
using System.Collections.Generic;
using System.Web.UI;
using MvcCms.Service.Code.Wiki;
using WikiPlex;
using WikiPlex.Formatting;

using MvcCms.Service.Code;
using MvcCms.Data;
using MvcCms.Service.Logging;
using MvcCms.Service;
using MvcCms.Service.Cache;
using MvcCmsForum.Service;
using MvcCms.Service.Validation;
using MvcCmsForum.Models.ViewData;
using MvcCms.Data.Pagination;
using MvcCms.Service.Filters;

namespace MvcCmsForum.Controllers
{
    [HandleError]
    [RedirectToNonSslIfNotAuthenticated(Redirect = true)]
    public class MvcCmsForumController : MvcCms.Service.Code.BaseClasses.CMSControllerBase
    {

        #region Constructors

        private readonly IForumService _forumService;
        private readonly IAccountService _accountService;
        private readonly Portal _currentPortal;
        private readonly IWikiEngine _wikiEngine;

        public MvcCmsForumController(IForumService forumService, ICacheService cacheService, IWikiEngine wikiEngine, IAccountService accountService, ICmsService cmsService, IZoneService zoneService)
            : base(cmsService, zoneService, accountService)
        {
            _forumService = forumService;
            _currentPortal = cacheService.GetCurrentPortal();
            _accountService = accountService;
            _wikiEngine = wikiEngine;
        }

        #endregion

        [CleanUrls]        
        public ActionResult Index()
        {
            IEnumerable<Forum> viewData = _forumService.ListForums();
            foreach (var forum in viewData)
            {
                forum.Description = _wikiEngine.Render(forum.Description, GetFormatter());
            }
            ViewData["PageTitle"] = _currentPortal.DisplayName + " Forums";
            ViewData["SkinName"] = _currentPortal.SkinName;
            return View(viewData);
        }

        [CleanUrls]
        public ActionResult ViewForum(string path, string page)
        {
            ViewData["message"] = "";
            int pagenum = 1;
            if (page != "1")
            {
                pagenum = int.Parse(page.Replace("page", ""));
            }
            var forum = _forumService.GetForum(path);
            int forumId = forum.ForumID;

            const int count = 10;
            int index = pagenum;

            var posts = _forumService.ListPosts(forumId);
            var pagedposts = posts.Where(p => p.Approved && p.ThreadParentPost == null).OrderByDescending(p => p.LastPostDate).AsPagination(index, count);

            ViewData["PageTitle"] = _currentPortal.DisplayName + " " + forum.Title + " Forum";
            ViewData["SkinName"] = _currentPortal.SkinName;
            var model = new ForumModel
            {
                _CurrentPortal = _currentPortal,
                _Forum = forum,
                _Posts = pagedposts
            };

            return View(model);
        }

        [CleanUrls]
        public ActionResult ViewPost(string forumpath, string path, string page)
        {
            ViewData["message"] = "";

            var forum = _forumService.GetForum(forumpath);
            int forumId = forum.ForumID;

            var post = _forumService.GetPost(path, forumId);
            int pageNumber = 1;
            ViewData["PageTitle"] = _currentPortal.DisplayName + " " + post.Title;
            if (page != "1")
            {
                pageNumber = int.Parse(page.Replace("page", ""));
                ViewData["PageTitle"] = _currentPortal.DisplayName + " " + post.Title + " Page " + pageNumber;
            }

            int postId = post.PostID;
            var dtoPost = _forumService.GetDTOPost(postId);
            //this could be done in the service or in repository
            post.ViewCount++;
            _forumService.EditPost(post);

            const int count = 5;
            int index = pageNumber;
            
            ViewData["count"] = count;
            ViewData["index"] = index;
            var user = _accountService.GetUser();
            if (user != null)
            {
                var vote = _forumService.GetVote(postId, user.UserID);

                ViewData["userVote"] = vote == null ? (short)0 : vote.Direction;
            }
            else
            {
                ViewData["userVote"] = 0;
            }

            var replies = _forumService.ListReplies(postId, index);

            foreach (var reply in replies)
            {
                reply.Body = _wikiEngine.Render(reply.Body, GetFormatter());
                foreach (var reply1 in reply.Replies)
                {
                    reply1.Body = _wikiEngine.Render(reply1.Body, GetFormatter());
                }
            }

            ViewData["NoIndex"] = false;
            //if this post is a reply with no replies put a noindex on it
            if (dtoPost.ThreadParentPost != null)
            {
                if (dtoPost.ReplyCount == 0)
                {
                    ViewData["NoIndex"] = true;
                }
            }
            dtoPost.Body = _wikiEngine.Render(dtoPost.Body, GetFormatter());
            ViewData["SkinName"] = _currentPortal.SkinName;
            var model = new ForumModel
            {
                _CurrentPortal = _currentPortal,
                _Forum = forum,
                _Posts = replies,
                _Post = dtoPost
            };

            return View(model);
        }

        private MacroFormatter GetFormatter()
        {
            var siteRenderers = new IRenderer[] 
            { 
                new TitleLinkRenderer(Url), 
                new TextColorRenderer() ,
                new TextSizeRenderer(),
                new AnyStyleRenderer(),
            };
            IEnumerable<IRenderer> allRenderers = Renderers.All.Union(siteRenderers);
            return new MacroFormatter(allRenderers);
        }

        [Authorize]
        public JsonResult MarkAnswer(int postId)
        {
            var post = _forumService.GetPost(postId);
            var user = _accountService.GetUser();
            if (post.ThreadUser.UserName != user.UserName || !Roles.IsUserInRole("Moderator"))
            {
                return Json(new { notauthorized = true });
            }
            post.IsAnswer = true;
            post.ThreadParentPost.IsAnswered = true;
            _forumService.EditPost(post);
            return Json(new { success = true });
        }

        [Authorize]
        public JsonResult Vote(int postId, int direction)
        {
            var post = _forumService.GetPost(postId);
            var user = _accountService.GetUser();            
            var vote = _forumService.GetVote(postId, user.UserID);
            if (vote == null)
            {
                vote = new Vote
                {
                    User = user,
                    AddedDate = DateTime.Now,
                    AddedByIP = Request.UserHostAddress,
                    Direction = (direction > 0) ? (short)1 : (short)-1,
                };
                post.Votes.Add(vote);
                post.VoteCount += vote.Direction;

                _forumService.EditPost(post);
            }
            bool isreply = false;
            if (post.ThreadParentPost != null)
            {
                isreply = true;
            }
            return Json(new { isReply = isreply, postId = postId, direction = vote.Direction, voteCount = post.VoteCount });
        }

        [Authorize(Roles = "Admin, Editor")]
        public ActionResult ManageForums()
        {
            var viewData = _forumService.ListForums();
            foreach (var forum in viewData)
            {
                forum.Description = _wikiEngine.Render(forum.Description, GetFormatter());
            }
            ViewData["PageTitle"] = _currentPortal.DisplayName + " Manage Forums";
            ViewData["SkinName"] = _currentPortal.SkinName;
            return View(viewData);
        }

        [Authorize(Roles = "Admin, Editor")]
        [ValidateInput(false)]
        public ActionResult CreateForum(string title, string description, int? order, bool? moderated)
        {
            ViewData["message"] = "";
            order = order ?? 0;
            moderated = moderated ?? false;

            if (!String.IsNullOrEmpty(title) && !String.IsNullOrEmpty(description))
            {
                try
                {
                    var forum = new Forum
                    {
                        ForumAdmin = _accountService.GetUser(User.Identity.Name),
                        AddedDate = DateTime.Now,
                        Title = title,
                        Path = title.ToUrlFormat(),
                        Description = description,
                        Importance = order.Value,
                        Moderated = moderated.Value
                    };
                    if (_forumService.CreateForum(forum))
                    {
                        var newforum = _forumService.GetForum(forum.Path);
                        ViewData["message"] = "Your forum has been created.";
                        return RedirectToAction("EditForum", new { forumId = newforum.ForumID });
                    }
                    ViewData["message"] = "There is already a forum with this name.";
                }
                catch (Exception exc)
                {
                    ViewData["message"] = exc.Message;
                }
            }

            ViewData["title"] = title;
            ViewData["description"] = description;
            ViewData["order"] = order;
            ViewData["moderated"] = moderated;

            ViewData["PageTitle"] = _currentPortal.DisplayName + " Create Forum";
            ViewData["SkinName"] = _currentPortal.SkinName;
            return View("CreateForum");
        }

        [Authorize(Roles = "Admin, Editor")]
        [ValidateInput(false)]
        public ActionResult EditForum(int forumId, string title, string description, int? order, bool? moderated)
        {
            ViewData["message"] = "Your forum has been updated.";
            order = order ?? 0;
            moderated = moderated ?? false;

            Forum forum = _forumService.GetForum(forumId);

            if (!String.IsNullOrEmpty(title) && !String.IsNullOrEmpty(description))
            {
                try
                {
                    forum.Title = title;
                    forum.Description = description;
                    forum.Importance = order.Value;
                    forum.Moderated = moderated.Value;

                    ViewData["message"] = _forumService.EditForum(forum) ? "Your forum has been updated." : "There was a problem updating the forum.";
                }
                catch (Exception exc)
                {
                    ViewData["message"] = exc.Message;
                }
            }

            ViewData["PageTitle"] = _currentPortal.DisplayName + " Edit Forum";
            ViewData["SkinName"] = _currentPortal.SkinName;
            return View("EditForum", forum);
        }

        [Authorize(Roles = "Admin, Editor")]
        public ActionResult RemoveForum(int forumId, string remove)
        {
            Forum forum = _forumService.GetForum(forumId);

            if (String.Equals(remove, "yes", StringComparison.OrdinalIgnoreCase))
            {
                _forumService.DeleteForum(forum);

                TempData["SuccessMessage"] = "The forum, " + forum.Title + ", has been deleted.";

                forum = null;
            }
            else if (String.Equals(remove, "no", StringComparison.OrdinalIgnoreCase))
            {
                TempData["InformationMessage"] = "The forum, " + forum.Title + ", has NOT been deleted.";
            }
            else
            {
                TempData["WarningMessage"] = "Are you sure you want to delete " + forum.Title + ".  You will not be able to recover this forum if you select YES.";
            }

            ViewData["PageTitle"] = _currentPortal.DisplayName + " Remove Forum";
            ViewData["SkinName"] = _currentPortal.SkinName;
            return View(forum);
        }

        [Authorize]
        [ValidateInput(false)]
        public ActionResult CreatePost(int? forumId, int? parentPostId, string title, string body)
        {
            Forum forum = null;
            Post parentPost = null;

            if (forumId.HasValue)
            {
                forum = _forumService.GetForum(forumId.Value);
                ViewData["Message"] = "You are creating a post in the \"" + forum.Title + "\" forum.";
            }

            if (parentPostId.HasValue)
            {
                parentPost = _forumService.GetPost(parentPostId.Value);

                forum = parentPost.Forum;
                ViewData["Message"] = "You are replying to \"" + parentPost.Title + "\" in the \"" + forum.Title + "\" forum.";
            }

            if (!String.IsNullOrEmpty(title) && !String.IsNullOrEmpty(body))
            {
                int newpostId;
                Post parentpost = null;
                if (parentPostId.HasValue)
                {
                    parentpost = _forumService.GetPost(parentPostId.GetValueOrDefault());
                }

                var post = new Post
                {
                    ThreadUser = _accountService.GetUser(User.Identity.Name),
                    AddedDate = DateTime.Now,
                    AddedByIP = Request.UserHostAddress,
                    ThreadParentPost = parentpost,
                    Title = title,
                    Path = title.ToUrlFormat(),
                    Body = body,
                    Forum = forum,
                    ViewCount = 1,
                    ReplyCount = 0,
                    VoteCount = 0,
                    Approved = !forum.Moderated,
                    ReplyUser = _accountService.GetUser(User.Identity.Name),
                    LastPostDate = DateTime.Now
                };

                if (parentPost != null)
                {
                    parentPost.ReplyUser = _accountService.GetUser(User.Identity.Name);
                    parentPost.LastPostDate = DateTime.Now;
                    parentPost.ReplyCount++;
                    _forumService.EditPost(parentPost);
                    newpostId = post.PostID;
                    post.Path = post.Path + "-" + newpostId;
                    post.ReplyNumber = parentpost.ReplyCount;
                    _forumService.EditPost(parentPost);
                }
                else
                {
                    post.Forum = forum;
                    newpostId = _forumService.CreatePost(post);
                }
                if (newpostId != 0)
                {
                    ViewData["Message"] = forum.Moderated ? "Your post has been created and is awaiting approval from a moderator." : "Your post has been created.";

                    if (parentPost != null)
                    {
                        int page = (post.ReplyNumber.GetValueOrDefault() / 6);
                        if (page > 0)
                        {
                            page += 1;
                            parentPost.Path = post.ThreadParentPost.Path + "/page" + page + "/#pst-" + post.PostID;
                        }
                        return Redirect("/forums/forum-" + parentPost.Forum.Path + "/" + parentPost.Path);
                    }
                    return forum.Moderated ? RedirectToAction("ViewForum", new { path = forum.Path }) : RedirectToAction("ViewPost", new { forumpath = post.Forum.Path, path = post.Path });
                }
                ViewData["Message"] = "There is already a post in this forum with that title.";
            }

            ViewData["title"] = title;
            ViewData["body"] = body;
            ViewData["ForumID"] = forumId;
            ViewData["PageTitle"] = _currentPortal.DisplayName + " Create Post";
            ViewData["SkinName"] = _currentPortal.SkinName;
            return View("CreatePost");
        }

        [Authorize]
        [ValidateInput(false)]
        public ActionResult EditPost(bool isReply, int editedPostID, string source, string pageNo)
        {           
            var user = _accountService.GetUser();
            var post = _forumService.GetPost(editedPostID);
            if (post.ThreadUser.UserID != user.UserID)
            {
                if (!Roles.IsUserInRole("Admin"))
                {
                    if (!Roles.IsUserInRole("Editor"))
                    {
                        if (!Roles.IsUserInRole("Moderator"))
                        {
                            RedirectToAction("Index", "MvcCmsForum");
                        }
                    }
                }
            }
            post.Body = source;
            post.EditDate = DateTime.Now;
            _forumService.EditPost(post);
            if (isReply)
            {
                return RedirectToAction("ViewPost", new { forumpath = post.Forum.Path, path = post.ThreadParentPost.Path, page = pageNo });
            }
            return RedirectToAction("ViewPost", new { forumpath = post.Forum.Path, path = post.Path, page = pageNo });
        }

        [Authorize(Roles = "Admin, Editor")]
        public JsonResult RemovePost(int postId)
        {
            //need to do some stuff here also to fix up reply number for everything behind this post being deleted or else just don't allow a post to be deleted that has posts after it
            var post = _forumService.GetPost(postId);
            var parent = post.ThreadParentPost;
            if (parent != null)
            {
                parent.ReplyCount = parent.ReplyCount - 1;
                _forumService.EditPost(parent);
            }
            _forumService.DeletePost(post);
            return Json(new { postId = postId });
        }

        [AcceptVerbs(HttpVerbs.Post)]
        [OutputCache(Location = OutputCacheLocation.None)]
        public string GetPostSource(int postId)
        {
            var post = _forumService.GetPost(postId);

            return post.Body;
        }

        [AcceptVerbs(HttpVerbs.Post)]
        [OutputCache(Location = OutputCacheLocation.None)]
        [ValidateInput(false)]
        public string GetPostPreview(int postId, string source)
        {
            return _wikiEngine.Render(source, GetFormatter());
        }

        public ActionResult FAQ()
        {
            return View();
        }

    }
}
